IndexOutofBounexception in search adapter onbind method?

I keep getting IndexOutofBoundsException from my onBind method in search adapter as soon as I start to search /filter my recyclerview list results. Here's the exception :

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 0(offset:0).state:25 android.support.v7.widget.RecyclerView{3247b3d VFED..... ......ID 0,0-1440,5550 #7f090216 app:id/search_guests_recycler_view}, adapter:com.myapp.ui.registervisitor.searchGuests.SearchGuestListAdapter@2433f32, layout:android.support.v7.widget.LinearLayoutManager@68f5483, context:com.myapp.ui.registervisitor.searchGuests.SearchGuestActivity@843713
        at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5923)
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5858)
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5854)
        at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2230)
        at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1557)
        at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1517)
        at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:612)
        at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
        at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:3336)
        at android.view.View.measure(View.java:24545)
        at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:735)
        at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:481)
        at android.view.View.measure(View.java:24545)
        at android.widget.ScrollView.measureChildWithMargins(ScrollView.java:1414)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
        at android.widget.ScrollView.onMeasure(ScrollView.java:452)
        at android.view.View.measure(View.java:24545)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
        at android.view.View.measure(View.java:24545)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:733)
        at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:805)
        at android.view.View.measure(View.java:24545)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:733)
        at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:805)
        at android.view.View.measure(View.java:24545)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
        at android.view.View.measure(View.java:24545)
        at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:735)
        at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:481)
        at android.view.View.measure(View.java:24545)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
        at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:143)
        at android.view.View.measure(View.java:24545)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6828)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
        at android.view.View.measure(View.java:24545)

I tried a couple of articles online, but no luck. some of the articles that I tried are : RecyclerView Adapter onBind method and RecyclerView: Inconsistency detected. Invalid item position

My adapter code for this issue is at : https://pastebin.com/VxsWWMiS

and corresponding activity code for filtering :

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                mSearchGuestListAdapter.getFilter().filter(query);

                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                mSearchGuestListAdapter.getFilter().filter(newText);
                mSearchGuestListAdapter.notifyDataSetChanged();
                mSearchGuestListAdapter.setFilter(newText);

                if(mSearchGuestListAdapter.getItemCount() == 0){


                    String sourceString = "No match found for <b>" + newText + "</b> ";
                    mNoMatchTextView.setText(Html.fromHtml(sourceString));
                } else {
                    mEmptyRelativeLayout.setVisibility(View.GONE);
                    mRecyclerView.setVisibility(View.VISIBLE);
                }
                return false;
            }
        });

Happy to share any other details if needed. Any ideas how to resolve this?

Here's my entire activity code: https://pastebin.com/5qDN4yh9

This is the presenter class where the API is called : https://pastebin.com/YGiPGn8Z

This is the model class: https://pastebin.com/WCkPFnvU

This is a json sample : https://pastebin.com/82R1zBHP

Here's the xml code where recyclerview is called : https://pastebin.com/Z3QxPkSL "search_guests_recycler_view"

There are multiple considerations in your code that may cause that inconsistency in your adapter.

To understand how Filter class works you should note that, according to official documentation:

Filtering operations performed by calling filter(java.lang.CharSequence) or filter(java.lang.CharSequence, android.widget.Filter.FilterListener) are performed asynchronously. When these methods are called, a filtering request is posted in a request queue and processed later. Any call to one of these methods will cancel any previous non-executed filtering request.


1. Use a single instance of Filter

The Filter class performs a background task in which the performFiltering() method is called. To avoid overlapping filters while typing you should avoid using multiple instances of Filter. In your SearchGuestListAdapter class:

Incorrect:

@Override
public Filter getFilter() {
    // WRONG: Return new instance of Filter every time getFilter() is called
    return new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence charSequence) {
            // Perform filtering...
            return anything;
        }

        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
            // Update and Notify adapter...
        }
    };
}

Instead replace with:

private final Filter filter = new Filter() {
    @Override
    protected FilterResults performFiltering(CharSequence charSequence) {
        // Perform filtering...
        return anything;
    }

    @Override
    protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
        // Update and Notify adapter...
    }
};

@Override
public Filter getFilter() {
    // CORRECT: Always use the same Filter instance.
    return this.filter;
}

2. You should not modify adapter variables in performFiltering()

performFiltering() method is executed in a background thread. Here you can do a long job, read data from a database, even make a webservice synchronous request if you need it.

But, if you alter the adapter list here you will cause inconsistency between RecyclerView is showing on the screen and the contents of the adapter list. Instead you must build a temporary list that you will return using FilterResults.

Fix performFiltering() with the following structure:

@Override
protected FilterResults performFiltering(CharSequence charSequence) {
    // Here is Background Thread, never alter adapter list in this method
    String charString = charSequence.toString();
    List<RegisterGuestList.Guest> filteredList = new ArrayList<>();

    if (charString.isEmpty()) {
        filteredList.addAll(mSearchGuestListResponseList);
    } else {
        ... // fill filteredList as you did previously
    }

    FilterResults filterResults = new FilterResults();
    filterResults.values = filteredList;
    filterResults.count = filteredList.size(); // count is optional
    return filterResults;
}

@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
    // Here is Main Thread, safe to update list and notify adapter
    mSearchGuestListResponseListFiltered = (ArrayList<RegisterGuestList.Guest>) filterResults.values;
    searchText = charSequence.toString();
    notifyDataSetChanged();
}

3. Use FilterListener to get notified when filter completed.

In your onQueryTextChange() of SearchGuestActivity, you want check adapter list size to show or hide a empty view. As Filter works in background thread you must perform filter call with a FilterListener to check empty list.

Remove your setFilter() method of SearchGuestListAdapter and Fix onQueryTextChange() of SearchGuestActivity with the following structure:

@Override
public boolean onQueryTextChange(String newText) {
    mSearchGuestListAdapter.getFilter().filter(newText, new Filter.FilterListener() {
        @Override
        public void onFilterComplete(int count) {
            if (count == 0) {
                // Show empty view...
            } else {
                // Show recyclerView...
            }
        }
    });

    // Don't call notifyDataSetChanged() or setFilter() here!
    // Adaper will notified by publishResults() method
    // mSearchGuestListAdapter.setFilter(newText);
    // mSearchGuestListAdapter.notifyDataSetChanged();

    return false;
}

To provide binding for a service, you must implement the onBind() callback method. This method returns an IBinder object that defines the programming interface that clients can use to interact with the service. Binding to a started service. As discussed in the Services document, you can create a service that is both started and bound.

The stack trace above, seems like doesn't related on your code implicitly. And issue caused by internal RecyclerView implementation of animations. Also described here.

1) Possible workarounds for it, just clearing active ViewHolders pool, which could be pending on animation. And could throw IndexOfBoundException. Call below methods, before any notifyDataSetChanged.

mRecyclerView.getRecycledViewPool().clear();
mAdapter.notifyDataSetChanged();

2) Make sure you don't have notifyItemRangeChanged or notifyItemAdded(and similar) methods in your adapters. Use notifyDataSetChanged. Of course you can avoid this, but you need to use single collection and debug correct position every time to use this features.

3) And finally as you can see, it's all about animation for your items and internal behavior for RecyclerView. To resolve it from another side, you could override LayoutManager and remove animation. This will ensure, there is no issue with positions within your implementation.

/**
 * Could be the same for Grid LayoutManager or Linear
 **/
private static class NpaLinearLayoutManager extends LinearLayoutManager {
    /**
     * Disable predictive animations. There is a bug in RecyclerView which causes views that
     * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the
     * adapter size has decreased since the ViewHolder was recycled.
     */
    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }
// .....
}

I believe the answer to this is "no", but is there a method/service in Angular where I can pass in a component's root DOM node (e.g. <foo-component>) and receive the component instance (e.g. FooComponent)? I couldn't find an associated SO post on this. Example:

Replace all occurrences of mSearchGuestListResponseList in SearchGuestListAdapter#getItemCount(), and in SearchGuestListAdapter#getItemViewType() with mSearchGuestListResponseListFiltered;

I am looking for some solution that help to search term from html string with highlight feature. I can do this by removing html content from string. But then issue is I will not able to see it original content with highlight. I do have following function that can search & highlight string without html markup.

This method is called right when the adapter is created and is used to initialize your ViewHolder(s). One of the interesting things and, in my opinion, one of the more powerful features that come baked into the RecyclerView , is that the getItemViewType(int position) method allows us to return the actual layout id that the Android framework

Now inside onClick() method, we can write our logic. I have made public static text view and dialog variables in the main activity. so we can use these two instances in this adapter class. Text view will show the name of the clicked item and dialog will be removed then after. Step 4. Final Changes in code. Now we are left with two files.

How to properly use smbj to connect and list files on a samba share in Android java?

Comments
  • use an adapter that already implements Filterable, why to reinvent the wheel? for example, this is the complete, working Filterable adapter: pastebin.com/raw/LEena3pM
  • I do already use implements Filterable in my adapter
  • thats why i said: "why to reinvent the wheel?" do you want to implement the same code in every adapter you want to be filtered?
  • I don't have any other code that requires filtering, but since this seems to be working as expected for my filter needs, can you help figure out what i might be missing that could be causing the crash? I wondr if it's something as simple as adding notifydatasetchanged or updating the position of the item? tried a coupl of things but no luck
  • you dont set filterResults.count - but i am not 100% sure if this is the only bug in your custom Filter
  • Is there anything for a linearlayoutmanager? how do i inject it into my code?
  • also, in my activity I do call : Objects.requireNonNull(mRecyclerView).getRecycledViewPool().clear(); every single time i call notifydatasetchanged. I do not use notifyItemRangeChanged or itemadded. I do setitemanimator to null while setting adapter in activity. tried all solutions including that link, no luck so far :(
  • @AngelaHeely Check the answer. As I mentioned it could be done for any LayoutManager.
  • @AngelaHeely Did you have chance to try it?
  • i did, still crashes for me. not sure whats missing :(
  • do you happen to have any ides about this : stackoverflow.com/questions/58629974/…
  • @AngelaHeely the link is dead
  • sorry, here's the other link : stackoverflow.com/questions/58632013/…
  • do you also happen to know if there's any sample app that displays a timeline for the date on calendar, in minutes and you can select a slot to book or save an event?
  • any idea about this one ? stackoverflow.com/questions/58848391/…